package com.zhuanzhuan.hero.user.service.impl;

import com.bj58.zhuanzhuan.zzentry.common.service.entity.ServiceHeaderEntity;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Maps;
import com.zhuanzhuan.hero.biz.common.entity.risk.control.request.FellowCertificateLabelRiskControlRequest;
import com.zhuanzhuan.hero.biz.common.entity.risk.control.response.RiskControlResult;
import com.zhuanzhuan.hero.biz.common.enums.RiskControlResultType;
import com.zhuanzhuan.hero.social.interaction.enums.EAttentionRelationEnum;
import com.zhuanzhuan.hero.user.consts.Consts;
import com.zhuanzhuan.hero.user.consts.NumConsts;
import com.zhuanzhuan.hero.user.dto.BasePageQueryDTO;
import com.zhuanzhuan.hero.user.dto.label.CancelAuthenticatedLabelDTO;
import com.zhuanzhuan.hero.user.entity.HeroLabelInfo;
import com.zhuanzhuan.hero.user.entity.HeroUserLabelAuthRecord;
import com.zhuanzhuan.hero.user.entity.HeroUserLabelRelation;
import com.zhuanzhuan.hero.user.enums.*;
import com.zhuanzhuan.hero.user.exception.BusinessException;
import com.zhuanzhuan.hero.user.helper.HeroSocialHelper;
import com.zhuanzhuan.hero.user.helper.PushHelper;
import com.zhuanzhuan.hero.user.pojo.bo.CreateAuthRecordBO;
import com.zhuanzhuan.hero.user.pojo.bo.CreateLabelBO;
import com.zhuanzhuan.hero.user.pojo.bo.LabelInfoQueryBO;
import com.zhuanzhuan.hero.user.pojo.bo.UserAttentionBO;
import com.zhuanzhuan.hero.user.pojo.po.LabelInfoPO;
import com.zhuanzhuan.hero.user.service.*;
import com.zhuanzhuan.hero.user.util.StringUtil;
import com.zhuanzhuan.hero.user.vo.PageResultWrapper;
import com.zhuanzhuan.hero.user.vo.UserItemBriefVO;
import com.zhuanzhuan.hero.user.vo.UserListItemVO;
import com.zhuanzhuan.hero.user.vo.label.CertificationQualifiedVO;
import com.zhuanzhuan.hero.user.vo.label.FraternityAuthLabelVO;
import com.zhuanzhuan.hero.user.vo.label.ToAuthFraternityAuthLabelVO;
import com.zhuanzhuan.hero.user.vo.label.UserFraternityAuthLabelVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @ClassName FraternityAuthServiceImpl
 * @Description
 * @Author hanjunjun
 * @Date 2024/7/2 17:17
 */
@Service
@Slf4j
public class FraternityAuthServiceImpl implements IFraternityAuthService {

    @Resource
    private HeroUserLabelRelationService userLabelRelationService;

    @Resource
    private HeroLabelInfoService labelInfoService;

    @Resource
    private HeroSocialHelper heroSocialHelper;

    @Resource
    private HeroUserLabelAuthRecordService userLabelAuthRecordService;

    @Resource
    private ICounterService counterService;

    @Resource
    private IRiskControlService riskControlService;

    @Resource
    private IUserService userService;

    @Resource
    private IPushService pushService;

    @Resource
    private IApolloService apolloService;

    @Override
    public List<FraternityAuthLabelVO> queryBoundedFraternityAuthLabels(Long uid) {
        log.info("queryBoundedFraternityAuthLabels uid={}", uid);
        List<LabelInfoPO> relatedLabelInfos =
            userLabelRelationService.queryUserRealateLabelInfos(Collections.singletonList(uid), LabelTypeEnum.FRATERNITY_AUTH.getCode());
        if (CollectionUtils.isEmpty(relatedLabelInfos)) {
            return new ArrayList<>();
        }

        return relatedLabelInfos.stream()
            .map(this::convertFraternityAuthLabelVO)
            .collect(Collectors.toList());
    }


    @Override
    public Map<Long, List<FraternityAuthLabelVO>>  queryBoundedFraternityAuthLabels(List<Long> uids) {
        List<LabelInfoPO> relatedLabelInfos =
            userLabelRelationService.queryUserRealateLabelInfos(uids, LabelTypeEnum.FRATERNITY_AUTH.getCode());
        if (CollectionUtils.isEmpty(relatedLabelInfos)) {
            return Maps.newHashMap();
        }

        Map<Long, List<LabelInfoPO>> groupedByUid = relatedLabelInfos.stream().collect(Collectors.groupingBy(LabelInfoPO::getUid));
        return groupedByUid.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
                        entry -> entry.getValue().stream().map(this::convertFraternityAuthLabelVO).collect(Collectors.toList())
                ));
    }



    @Transactional(rollbackFor = Exception.class)
    @Override
    public UserFraternityAuthLabelVO addFraternityAuthLabel(Long uid, String labelName, ServiceHeaderEntity header) {
        LabelInfoQueryBO queryBO =
            LabelInfoQueryBO.builder().labelType(LabelTypeEnum.FRATERNITY_AUTH.getCode()).createUid(uid).build();
        List<HeroLabelInfo> labelInfos = labelInfoService.listByParam(queryBO);
        //最多10个
        if (labelInfos.size() >= NumConsts.TEN) {
            throw new BusinessException("最多创建10个同行认证标签");
        }
        //重名校验
        if (labelInfos.stream().anyMatch(labelInfo -> labelInfo.getLabelName().equals(labelName))) {
            throw new BusinessException("该标签已存在");
        }

        //保存
        CreateLabelBO createLabelBO = new CreateLabelBO();
        createLabelBO.setLabelType(LabelTypeEnum.FRATERNITY_AUTH.getCode())
            .setLabelName(labelName)
            .setCreaterUid(uid)
            .setCreaterRole(CreaterRoleEnum.CUSTOMER.getCode());
        Long labelId = labelInfoService.createLabel(createLabelBO);

        //风控
        FellowCertificateLabelRiskControlRequest riskControlRequest = new FellowCertificateLabelRiskControlRequest();
        riskControlRequest.setLabelName(labelName);
        RiskControlResult riskControlResult =
            riskControlService.fraternityAuthLabelRiskControl(uid, labelId, header, riskControlRequest);
        if (RiskControlResultType.PASS.getCode() != riskControlResult.getResult()) {
            log.info("新增同行认证标签风控不通过");
            throw new BusinessException(ExceptionMsgEnum.BUSINESS_ERROR.getCode(), Consts.RISK_CONTROL_FAIL_TPS);
        }

        UserFraternityAuthLabelVO resultVO = new UserFraternityAuthLabelVO();
        resultVO.setLabelId(labelId).setLabelName(labelName).setIsBounded(false).setCanDel(true);
        return resultVO;
    }

    @Override
    public void delFraternityAuthLabel(Long uid, Long labelId) {
        labelInfoService.delByIds(Collections.singletonList(labelId), LabelTypeEnum.FRATERNITY_AUTH.getCode(),
            uid, true);
    }

    @Override
    public List<UserFraternityAuthLabelVO> queryUserFraternityAuthLabels(Long uid) {
        //查询同行认证标签  运营和用户自己创建的
        List<HeroLabelInfo> labelInfos = labelInfoService.queryAllFraternityAuthLabels(uid);

        //查询关联关系  是已绑定的
        List<HeroUserLabelRelation> labelRelations =
            userLabelRelationService.queryUserRealateLabels(uid, LabelTypeEnum.FRATERNITY_AUTH.getCode());
        List<Long> boundedLabelIds =
            labelRelations.stream().map(HeroUserLabelRelation::getLabelId).collect(Collectors.toList());

        //转换
        return labelInfos.stream()
            .map(labelInfo -> this.convertUserFraternityAuthLabelVO(labelInfo, boundedLabelIds))
            .collect(Collectors.toList());
    }

    @Override
    public void bindFraternityAuthLabels(Long uid, List<Long> labelIds) {
        userLabelRelationService.userRelateLabels(uid, LabelTypeEnum.FRATERNITY_AUTH.getCode(), labelIds);
    }

    @Override
    public CertificationQualifiedVO hasCertificationQualified(Long uid, Long homemakerUid) {
        List<UserAttentionBO> userAttentions =
            heroSocialHelper.queryserAttentions(uid, Collections.singletonList(homemakerUid));
        UserAttentionBO userAttention =
            userAttentions.stream()
                .filter(attention -> attention.getToUid().equals(homemakerUid)).findFirst()
                .orElse(null);

        String noQualifiedTips = apolloService.queryTips().getFraternityAuthNoQualified();

        if (Objects.isNull(userAttention)) {
            log.info("没有关系等级，没有资格");
            return CertificationQualifiedVO.fail(noQualifiedTips);
        }

        //互关 or (单向关注&关注天数>=7)
        Integer relationCode = userAttention.getAttentionRelationCode();

        boolean hasQualified =
            Arrays.asList(EAttentionRelationEnum.TWO_WAY_ATTENTION.getCode(), EAttentionRelationEnum.PARTNER.getCode())
                .contains(relationCode) || (EAttentionRelationEnum.ATTENTION.getCode()
                .equals(relationCode) && userAttention.getAttentionDay() >= NumConsts.SEVEN);
        if (hasQualified) {
            return CertificationQualifiedVO.success();
        }
        return CertificationQualifiedVO.fail(noQualifiedTips);
    }

    @Override
    public ToAuthFraternityAuthLabelVO queryToAuthFraternityAuthLabel(Long fromUid, Long toUid) {
        ToAuthFraternityAuthLabelVO vo = new ToAuthFraternityAuthLabelVO();
        //查询toUid所有已绑定的标签
        List<FraternityAuthLabelVO> boundedLabels = this.queryBoundedFraternityAuthLabels(toUid);
        if (CollectionUtils.isEmpty(boundedLabels)) {
            return vo;
        }
        Map<Long, FraternityAuthLabelVO> labelIdMap =
            boundedLabels.stream().collect(Collectors.toMap(FraternityAuthLabelVO::getLabelId, Function.identity()));

        //已认证的标签id
        Set<Long> authenticatedLabelIds = new LinkedHashSet<>();
        //查询认证记录  可能存在多条
        List<HeroUserLabelAuthRecord> authRecords =
            userLabelAuthRecordService.queryAuthRecord(fromUid, toUid, LabelTypeEnum.FRATERNITY_AUTH.getCode(),
                LabelAuthOperateTypeEnum.AUTH.getCode());
        if (CollectionUtils.isNotEmpty(authRecords)) {
            authRecords.forEach(authRecord -> {
                authenticatedLabelIds.addAll(StringUtil.str2List(authRecord.getLabelIds())
                    .stream()
                    .map(Long::parseLong)
                    .collect(Collectors.toList()));
            });
        }

        //装配
        List<FraternityAuthLabelVO> authenticatedLabels =
            authenticatedLabelIds.stream()
                .map(labelIdMap::get)
                //如果用户自定义标签被删除了，认证记录还存在labelId但是labelIdMap.get是为null的
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        vo.setAuthenticatedLabels(authenticatedLabels);

        List<FraternityAuthLabelVO> unauthenticatedLabels = new ArrayList<>();
        labelIdMap.forEach((labelId, labelVO) -> {
            if (!authenticatedLabelIds.contains(labelId)) {
                unauthenticatedLabels.add(labelVO);
            }
        });
        vo.setUnAuthenticatedLabels(unauthenticatedLabels);

        return vo;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void authenticatedLabels(Long fromUid, Long toUid, List<Long> labelIds) {
        labelIds = labelIds.stream().distinct().collect(Collectors.toList());
        //标签是否已经认证过了
        Set<Long> authedLabelIds = new HashSet<>();
        List<HeroUserLabelAuthRecord> authRecords =
            userLabelAuthRecordService.queryAuthRecord(fromUid, toUid, LabelTypeEnum.FRATERNITY_AUTH.getCode(),
                LabelAuthOperateTypeEnum.AUTH.getCode());
        authRecords.forEach(authRecord -> {
            authedLabelIds.addAll(StringUtil.str2List(authRecord.getLabelIds()).stream().map(Long::valueOf)
                .collect(Collectors.toSet()));
        });
        labelIds.forEach(labelId -> {
            if (authedLabelIds.contains(labelId)) {
                log.info("标签{}已经提交过认证了", labelId);
                throw new BusinessException(ExceptionMsgEnum.DUPLICATE_REQUEST.getCode(), "请勿重复认证");
            }
        });

        //新增认证记录
        CreateAuthRecordBO createAuthRecordBO = new CreateAuthRecordBO();
        createAuthRecordBO.setFromUid(fromUid).setToUid(toUid).setLabelIds(labelIds)
            .setLabelType(LabelTypeEnum.FRATERNITY_AUTH.getCode());
        userLabelAuthRecordService.createAuthRecord(createAuthRecordBO);

        //标签关联关系对应的认证数++
        userLabelRelationService.increCertifiedNum(toUid, labelIds);

        //上报计数  认证未读总数
        counterService.fraternityAuthUnreadIncre(toUid);

        //发push
        pushService.fraternithSendPush(fromUid, toUid);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void cancelAuthenticatedLabels(Long fromUid, CancelAuthenticatedLabelDTO cancelAuthenticatedLabelDTO) {
        Long toUid = cancelAuthenticatedLabelDTO.getHomemakerUid();
        List<Long> labelIds = cancelAuthenticatedLabelDTO.getLabelIds();

        //根据认证记录幂等
        labelIds = labelIds.stream().distinct().collect(Collectors.toList());
        //根据认证记录幂等
        List<HeroUserLabelAuthRecord> cancelAuthRecords =
            userLabelAuthRecordService.queryAuthRecord(fromUid, toUid, LabelTypeEnum.FRATERNITY_AUTH.getCode(),
                LabelAuthOperateTypeEnum.CANCEL_AUTH.getCode());
        if (CollectionUtils.isNotEmpty(cancelAuthRecords)) {
            for (HeroUserLabelAuthRecord cancelAuthRecord : cancelAuthRecords) {
                if (StringUtil.list2Str(labelIds.stream().map(String::valueOf).collect(Collectors.toList())).equals(cancelAuthRecord.getLabelIds())) {
                    log.info("已经提交过取消认证了");
                    throw new BusinessException(ExceptionMsgEnum.DUPLICATE_REQUEST.getCode(), "请勿重复操作");
                }
            }
        }

        //保存取消认证记录
        CreateAuthRecordBO createAuthRecordBO = new CreateAuthRecordBO();
        createAuthRecordBO.setFromUid(fromUid).setToUid(toUid).setLabelIds(labelIds)
            .setLabelType(LabelTypeEnum.FRATERNITY_AUTH.getCode())
            .setCancelReason(cancelAuthenticatedLabelDTO.getCancelReason())
            .setCancelPics(cancelAuthenticatedLabelDTO.getCancelPics());
        userLabelAuthRecordService.createCancelAuthRecord(createAuthRecordBO);

        //标签关联关系对应的认证数--
        userLabelRelationService.decreCertifiedNum(toUid, labelIds);

    }

    @Override
    public boolean riskControlFail(Long labelId, List<String> rejectedFields) {
        log.info("同行认证标签风控失败处理 labelId={}", labelId);
        if (!rejectedFields.contains(RiskControlFieldEnum.FRATERNITY_AUTH_LABEL_NAME.getField())) {
            return true;
        }

        //查询
        HeroLabelInfo labelInfo = labelInfoService.queryById(labelId);
        if (Objects.isNull(labelInfo)) {
            log.info("标签不存在无需删除");
            return true;
        }

        //删除标签
        labelInfoService.delByIds(Collections.singletonList(labelId), LabelTypeEnum.FRATERNITY_AUTH.getCode(),
            NumConsts.SYSTEM_CODE, true);

        //发送push
        if (CollectionUtils.isNotEmpty(rejectedFields)) {
            rejectedFields.forEach(rejectField -> {
                String rejectTitle = riskControlService.parseTitleByField(rejectField);
                pushService.sendPushAsync(labelInfo.getCreateBy(), PushHelper.FRATERNITY_AUTH_RISK_TEMPLATE_ID,
                    PushHelper.parseParamMapByField(rejectTitle));
            });
        }

        return true;
    }

    @Override
    public PageResultWrapper<UserListItemVO> queryAuthenticatedUsers(Long uid, Integer pageNum, Integer pageSize) {
        PageInfo<HeroUserLabelAuthRecord> pageInfo = PageHelper.startPage(pageNum, pageSize).doSelectPageInfo(
            () -> userLabelAuthRecordService.queryAuthRecord(null, uid, LabelTypeEnum.FRATERNITY_AUTH.getCode(),
                LabelAuthOperateTypeEnum.AUTH.getCode()));

        BasePageQueryDTO basePageQueryDTO = new BasePageQueryDTO();
        basePageQueryDTO.setPageNum(pageNum);
        basePageQueryDTO.setPageSize(pageSize);

        List<HeroUserLabelAuthRecord> recordList = pageInfo.getList();
        if (CollectionUtils.isEmpty(recordList)) {
            return PageResultWrapper.empty(basePageQueryDTO);
        }

        List<Long> otherUids =
            recordList.stream().map(HeroUserLabelAuthRecord::getFromUid).distinct().collect(Collectors.toList());
        List<UserItemBriefVO> userItems= userService.buildUserItem(uid, otherUids, false, false);
        Map<Long, UserItemBriefVO> uidUserMap =
            userItems.stream().collect(Collectors.toMap(UserItemBriefVO::getUid, Function.identity()));

        List<UserListItemVO> voList =
            recordList.stream().map(authRecord -> this.convertUserListItemVO(authRecord, uidUserMap))
                .collect(Collectors.toList());

        return PageResultWrapper.wrapper(pageInfo.getTotal(), voList, pageInfo.isHasNextPage(), basePageQueryDTO);
    }

    private UserListItemVO convertUserListItemVO(HeroUserLabelAuthRecord authRecord, Map<Long, UserItemBriefVO> uidUserMap) {
        UserListItemVO vo = new UserListItemVO();
        vo.setId(authRecord.getId())
            .setUserInfo(uidUserMap.get(authRecord.getFromUid()));
        vo.setCreateTime(authRecord.getCreateTime());
        return vo;
    }

    private UserFraternityAuthLabelVO convertUserFraternityAuthLabelVO(HeroLabelInfo labelInfo, List<Long> boundedLabelIds) {
        UserFraternityAuthLabelVO vo = new UserFraternityAuthLabelVO();
        vo.setLabelId(labelInfo.getId());
        vo.setLabelName(labelInfo.getLabelName());
        vo.setIsBounded(boundedLabelIds.contains(labelInfo.getId()));
        vo.setCanDel(CreaterRoleEnum.CUSTOMER.getCode().equals(labelInfo.getCreaterRole()));
        return vo;
    }

    private FraternityAuthLabelVO convertFraternityAuthLabelVO(LabelInfoPO labelInfo) {
        FraternityAuthLabelVO vo = new FraternityAuthLabelVO();
        vo.setLabelId(labelInfo.getLabelId());
        vo.setLabelName(labelInfo.getLabelName());
        vo.setCertifiedNum(labelInfo.getCertifiedNum());
        return vo;
    }

    @Override
    public Integer queryAuthenticatedNum(Long uid) {
        //select
        //        id,count(distinct from_uid) auth_num
        //from
        //        hero_user_label_auth_record
        //where
        //        to_uid = 496862624092917760
        //        and label_type = 4
        //        and operate_type = 1
        //        and state = 1
        Map<Long, Integer> uidAuthNumMap = userLabelAuthRecordService.queryUidAuthNum(Collections.singletonList(uid),
            LabelTypeEnum.FRATERNITY_AUTH.getCode(), LabelAuthOperateTypeEnum.AUTH.getCode(), null);

        return uidAuthNumMap.getOrDefault(uid, NumConsts.ZERO);
    }
}
